package com.study.zip.testzip;

import net.lingala.zip4j.exception.ZipException;
import net.lingala.zip4j.model.ZipParameters;
import net.lingala.zip4j.util.Zip4jConstants;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.tools.zip.ZipEntry;
import org.apache.tools.zip.ZipFile;
import org.apache.tools.zip.ZipOutputStream;

import java.io.*;
import java.util.ArrayList;
import java.util.Enumeration;


/**
 *功能:zip压缩、解压
 *说明:本程序通过ZipOutputStream和ZipInputStream实现了zip压缩和解压功能.
 *问题:由于java.util.zip包并不支持汉字,当zip文件中有名字为中文的文件时,
 *     就会出现异常:"Exception  in thread "main " java.lang.IllegalArgumentException
 *               at   java.util.zip.ZipInputStream.getUTF8String(ZipInputStream.java:285)
 *解决:
 *　　方法1、修改import java.util.zip.ZipInputStream和ZipOutputStream.
 *          java.util.zip只支持UTF-8,Ant里面可以指定编码.
 *　　方法2、使用Apache Ant里提供的zip工具。
 *          不使用java.util.zip的包,把ant.jar放到classpath中.
 *          程序中使用import org.apache.tools.zip.*;
 *
 *仅供编程学习参考.
 *
 *@author Winty
 *@date   2008-8-3
 *@Usage:
 *   压缩:java Zip -zip "directoryName"
 *   解压:java Zip -unzip "fileName.zip"
 */

public class ZipUtil {
    private ZipFile zipFile;
	private ZipOutputStream zipOut;     //压缩Zip
	private ZipEntry zipEntry;
	private static int      bufSize;    //size of bytes
	private byte[]          buf;
	private int             readedBytes;

	public ZipUtil(){
		this(2048);
	}

	public ZipUtil(int bufSize){
		this.bufSize = bufSize;
		this.buf = new byte[this.bufSize];
	}
	
	
	//压缩文件夹内的文件
	public String doZip(String zipDirectory,String fileName) throws Exception{//zipDirectoryPath:需要压缩的文件夹名
		File file;
		File zipDir;

		zipDir = new File(zipDirectory);
		String zipFileName = fileName + ".zip";//压缩后生成的zip文件名

		try{
			this.zipOut = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(zipDir+File.separator+zipFileName)));
			zipOut.setEncoding("gbk");  
			handleDir(zipDir , this.zipOut,zipFileName);
			this.zipOut.close();
		}catch(Exception ioe){
			throw ioe;
		}
		return zipDirectory+zipFileName;
	}
	
	private void handleDir(File dir , ZipOutputStream zipOut, String zipFileName)throws IOException{
	    FileInputStream fileIn;
        File[] files;

        files = dir.listFiles();

        if(files.length == 0){//如果目录为空,则单独创建之.
            //ZipEntry的isDirectory()方法中,目录以"/"结尾.
            this.zipOut.putNextEntry(new ZipEntry(dir.toString() + "/"));
            this.zipOut.closeEntry();
        }
        else{//如果目录不为空,则分别处理目录和文件.
            for(File fileName : files){
                if(fileName != null && fileName.getName().equals(zipFileName)){
                    continue;
                }
                //System.out.println(fileName);

                if(fileName.isDirectory()){
                    continue;
                }
                else{
                    fileIn = new FileInputStream(fileName);
                  
                    this.zipOut.putNextEntry(new ZipEntry(fileName.getName()));

                    while((this.readedBytes = fileIn.read(this.buf))>0){
                        this.zipOut.write(this.buf , 0 , this.readedBytes);
                    }

                    this.zipOut.closeEntry();
                }
            }
        }
	}


	//由doZip调用,递归完成目录文件读取
    private void handleDirRec(File dir , ZipOutputStream zipOut)throws IOException{
        FileInputStream fileIn;
        File[] files;

        files = dir.listFiles();

        if(files.length == 0){//如果目录为空,则单独创建之.
            //ZipEntry的isDirectory()方法中,目录以"/"结尾.
            this.zipOut.putNextEntry(new ZipEntry(dir.toString() + "/"));
            this.zipOut.closeEntry();
        }
        else{//如果目录不为空,则分别处理目录和文件.
            for(File fileName : files){
                //System.out.println(fileName);

                if(fileName.isDirectory()){
                    handleDirRec(fileName , this.zipOut);
                }
                else{
                    fileIn = new FileInputStream(fileName);
                    this.zipOut.putNextEntry(new ZipEntry(fileName.toString()));

                    while((this.readedBytes = fileIn.read(this.buf))>0){
                        this.zipOut.write(this.buf , 0 , this.readedBytes);
                    }

                    this.zipOut.closeEntry();
                }
            }
        }
    }
    
  //解压指定zip文件 
    public void unZip(String unZipfileName){//unZipfileName需要解压的zip文件名 
        FileOutputStream fileOut; 
        File file; 
        InputStream inputStream; 

        try{ 
            this.zipFile = new ZipFile(unZipfileName);

            for(Enumeration entries = this.zipFile.getEntries(); entries.hasMoreElements();){ 
                ZipEntry entry = (ZipEntry)entries.nextElement();
                file = new File(entry.getName()); 

                if(entry.isDirectory()){ 
                    file.mkdirs(); 
                } 
                else{ 
                    //如果指定文件的目录不存在,则创建之. 
                    File parent = file.getParentFile(); 
                    if(!parent.exists()){ 
                        parent.mkdirs(); 
                    } 

                    inputStream = zipFile.getInputStream(entry); 

                    fileOut = new FileOutputStream(file); 
                    while(( this.readedBytes = inputStream.read(this.buf) ) > 0){ 
                        fileOut.write(this.buf , 0 , this.readedBytes ); 
                    } 
                    fileOut.close(); 

                    inputStream.close(); 
                }    
            } 
            this.zipFile.close(); 
        }catch(IOException ioe){ 
            ioe.printStackTrace(); 
        } 
    }
	
    public static String splitZipWithZipFile(File zipfile,Long splitFileSize) {
        if(zipfile == null) return null;
        return createSplitZipFile(zipfile.getParent(), zipfile.getName(),splitFileSize,null);
    }
    
    public static String splitZipWithPlainFile(File file,String zipName, Long splitFileSize,String key) {
        if(file == null) return null;
        return createSplitZipFile(file.getParent(), zipName,splitFileSize,key);
    }
    
    public static String createSplitZipFile(String dir,String fileName,Long splitFileSize,String key) {
        try {
            File spZipFile = new File(new StringBuilder(dir).append(File.separator).append("spzip").toString());
            if(spZipFile.exists()) {
                FileUtils.deleteDirectory(spZipFile);
            }
            spZipFile.mkdir();
            net.lingala.zip4j.core.ZipFile zipFile = new net.lingala.zip4j.core.ZipFile(new StringBuilder(spZipFile.getPath()).append(File.separator).append(fileName).toString());
            zipFile.setFileNameCharset("GBK");  
            ArrayList filesToAdd = new ArrayList();
            File parent = new File(dir);
            File[] sons = parent.listFiles();
            if(sons == null) return null;
            for(File f:sons) {
                if(f.isFile()) {
                    String name = f.getName();
                    String suffix = name.substring(name.lastIndexOf(".")+1,name.length());
                    if(!StringUtils.equals(suffix, "zip")) {
                        filesToAdd.add(f);
                    }
                }
            }
            
            ZipParameters parameters = new ZipParameters();
            parameters.setCompressionMethod(Zip4jConstants.COMP_DEFLATE);
            parameters.setCompressionLevel(Zip4jConstants.DEFLATE_LEVEL_NORMAL);
            if(StringUtils.isNotEmpty(key)) {
                parameters.setEncryptFiles(true);  
                parameters.setEncryptionMethod(Zip4jConstants.ENC_METHOD_AES);  
                parameters.setAesKeyStrength(Zip4jConstants.AES_STRENGTH_128);  
                parameters.setPassword(key.toCharArray()); 
            }

            zipFile.createZipFile(filesToAdd, parameters, true, splitFileSize);
            return spZipFile.getPath();
        } catch (ZipException e) {
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return dir;
    }

	//设置缓冲区大小
	public void setBufSize(int bufSize){
		this.bufSize = bufSize;
	}

	//测试Zip类
	public static void main(String[] args)throws Exception{
//	    String dir = splitZipWithZipFile(new File("D:\\download\\test\\ZipFile.zip"));
//	    System.out.println(dir);
	}
}

